package cn.lingyangwl.framework.mock.data.manager.impl;

import cn.lingyangwl.framework.mock.data.generator.MockDataGenerator;
import cn.lingyangwl.framework.mock.data.generator.MockDataRule;
import cn.lingyangwl.framework.mock.data.manager.GenMockDataManager;
import cn.lingyangwl.framework.mock.data.manager.MockDbDataManager;
import cn.lingyangwl.framework.mock.data.mapper.MockDbDataMapper;
import cn.lingyangwl.framework.mock.data.model.MysqlTable;
import cn.lingyangwl.framework.mock.data.model.MysqlTableColumn;
import cn.lingyangwl.framework.tool.core.StringUtils;
import lombok.extern.slf4j.Slf4j;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * @author shenguangyang
 */
@Slf4j
public class MockDbDataManagerImpl implements MockDbDataManager {
    @Resource
    private MockDbDataMapper mockDbDataMapper;

    @Resource
    private MockDataRule mockDataRule;

    @Resource
    private GenMockDataManager genMockDataManager;

    @Resource
    private Map<String, MockDataGenerator> dataGeneratorMap;

    @Override
    public List<MysqlTable> listDbTable() {
        return MysqlTable.from(mockDbDataMapper.listTable());
    }

    @Override
    public List<MysqlTableColumn> listDbTableColumn(String tableName) {
        return MysqlTableColumn.from(mockDbDataMapper.listTableColumn(tableName));
    }

    @Override
    public int mockDataAndSave(String tableName, int num) {
        List<MysqlTableColumn> tableColumnList = MysqlTableColumn.from(mockDbDataMapper.listTableColumn(tableName));
        // 获取字段名
        List<String> columnNameList = tableColumnList.stream().map(MysqlTableColumn::getColumnName)
                .collect(Collectors.toList());

        List<List<Object>> columnDataList = new ArrayList<>();
        Optional<MockDataGenerator> dataGenerator = dataGeneratorMap.values().stream().filter(v -> v.tableName().equals(tableName)).findFirst();
        for (int i = 0; i < num; i++) {
            Map<String, Object> dataMap = genMockDataManager.create(dataGenerator.orElse(null), tableColumnList);
            columnDataList.add(columnNameList.stream().map(dataMap::get).collect(Collectors.toList()));
        }

        return mockDbDataMapper.batchInsert(tableName, columnNameList, columnDataList);
    }

    @Override
    public String createMockDataGeneratorCode(String tableName) {
        List<MysqlTableColumn> tableColumnList = MysqlTableColumn.from(mockDbDataMapper.listTableColumn(tableName));
        StringBuilder respSb = new StringBuilder();
        String className = StringUtils.convertToCamelCase(tableName);
        // TODO 后期采用模板引擎生成
        respSb.append("@Component").append("\n");
        respSb.append(String.format("public class %s implements MockDataGenerator {", className)).append("\n");
        respSb.append("\t@Override").append("\n");
        respSb.append("\tpublic String tableName() {").append("\n");
        respSb.append("\t\treturn ").append("\"").append(tableName).append("\"").append(";\n");
        respSb.append("\t}\n\n");
        respSb.append("\t@Override\n");
        respSb.append("\tpublic Map<String, Function<MysqlTableColumn, Object>> data() {\n");
        respSb.append("int random = RandomUtil.randomInt(1, 100);").append("\n");
        respSb.append("Map<String, Function<MysqlTableColumn, Object>> resp = new HashMap<>();").append("\n");

        tableColumnList.forEach(tableColumn -> {
            String dataType = tableColumn.getDataType();
            String columnKey = tableColumn.getColumnKey();
            respSb.append(String.format("/* dataType: %s, comment: %s */",
                    tableColumn.getDataType(), tableColumn.getColumnComment())).append("\n");
            if (StringUtils.isNotEmpty(columnKey)) {
                respSb.append(String.format("resp.put(\"%s\", column -> YitIdHelper.nextId());",
                        tableColumn.getColumnName()));
            } else if ("varchar".equals(dataType) || "text".equals(dataType) || "char".equals(dataType)
                    || "blob".equals(dataType) || "tinytext".equals(dataType) || "tinyblob".equals(dataType)) {
                respSb.append(String.format("resp.put(\"%s\", MockHelper::mock);",
                        tableColumn.getColumnName()));
            } else if ("datetime".equals(dataType)) {
                respSb.append(String.format("resp.put(\"%s\", column -> LocalDateTime.now());",
                        tableColumn.getColumnName()));
            } else if ("date".equals(dataType)) {
                respSb.append(String.format("resp.put(\"%s\", column -> LocalDateTime.now());",
                        tableColumn.getColumnName()));
            } else if ("bigint".equals(dataType)) {
                respSb.append(String.format("resp.put(\"%s\", MockHelper::mock);", tableColumn.getColumnName()));
            } else if ("int".equals(dataType) || "tinyint".equals(dataType)) {
                respSb.append(String.format("resp.put(\"%s\", column -> Double.valueOf(MockHelper.mock(column)));", tableColumn.getColumnName()));
            } else if ("float".equals(dataType)) {
                respSb.append(String.format("resp.put(\"%s\", column -> Float.valueOf(MockHelper.mock(column)));", tableColumn.getColumnName()));
            } else if ("double".equals(dataType)) {
                respSb.append(String.format("resp.put(\"%s\", MockHelper::mock);", tableColumn.getColumnName()));
            } else if ("decimal".equals(dataType)) {
                respSb.append(String.format("resp.put(\"%s\", column -> new BigDecimal(MockHelper.mock(column)));",
                        tableColumn.getColumnName()));
            } else {
                log.warn("not find columnName [{}] dataType [{}]", tableColumn.getColumnName(), tableColumn.getDataType());
                respSb.append(String.format("resp.put(\"%s\", column -> \"1\");",
                        tableColumn.getColumnName()));
            }
            respSb.append("\n");
        });
        respSb.append("return resp;");
        respSb.append("\t}\n");
        respSb.append("}\n");
        return respSb.toString();
    }
}
